package com.citywithincity.pay.alipay;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.citywithincity.pay.IPay;
import com.citywithincity.pay.OrderInfo;
import com.citywithincity.pay.PayException;
import com.citywithincity.pay.PayFactory;
import com.citywithincity.pay.PayStatus;
import com.citywithincity.pay.SysOrder;
import com.citywithincity.utils.HttpUtil;
import com.citywithincity.utils.IoUtil;
import com.citywithincity.utils.MD5Util;
import com.citywithincity.utils.MapUtil;
import com.damai.xml.XMLParseException;
import com.damai.xml.XMLUtil;

public class ApiPayModel implements IPay {

	private static final Log logger = LogFactory.getLog(ApiPayModel.class);
	
	private static final String ENCODING = "utf-8";
	
	//安全教研吗
	private String safeKey;
	private String partner;
	private String seller;
	
	private String privateKey;
	
	
	public ApiPayModel(String partner,String seller,String privateKey,String safeKey) {
		this.partner = partner;
		this.seller = seller;
		this.privateKey = privateKey;
		this.safeKey = safeKey;
	}
	

	
	@Override
	public Object prePay(SysOrder order,
			String outID, 
			String title, 
			int fee, 
			int businessId,
			int type,
			String channel,
			HttpServletRequest request)
			throws PayException, IOException {
		StringBuilder sb = new StringBuilder(512);
		
		getOrderInfo(sb, title,  (double)fee/100, outID,getNotifyUrl(businessId,type));
		return getFinalString(sb);
	}
	
	private String getNotifyUrl(int businessId,int type){
		StringBuilder sb = new StringBuilder(50);
		String url = sb.append(PayFactory.getNotifyUrl()).append(businessId).append('-').append(type).toString();
		return url;
	}
	
	private String getFinalString(StringBuilder sb) throws PayException{
		// 对订单做RSA 签名
		String sign = sign(sb.toString());
		try {
			// 仅需对sign 做URL编码
			sign = URLEncoder.encode(sign, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new PayException(e.getMessage(),"UTF8Encoding");
		}
		sb.append("&sign=\"").append(sign).append("\"&sign_type=\"RSA\"");
		return sb.toString();
	}
	
	private String sign(String content) {
		return RSA.sign(content, privateKey);
	}

	@Override
	public boolean refund(SysOrder order,String orderID, String refundID, int fee, boolean isOutID) throws PayException, IOException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public PayStatus queryStatus(String orderID,SysOrder order, boolean isOutID) throws PayException, IOException {
		throw new RuntimeException("Not implemented!");
	}
	
	private static final String QUERY_URL = "https://mapi.alipay.com/gateway.do?service=single_trade_query&sign=";
	
	@Override
	public OrderInfo queryOrder(SysOrder order,String orderID, boolean isOutID) throws PayException, IOException {

	    Map<String, String> data = new TreeMap<String, String>();
	    data.put("service", "single_trade_query");
	    data.put("partner", partner);
	    data.put("_input_charset",ENCODING);
	    data.put("out_trade_no",orderID);
	    StringBuilder sb = new StringBuilder(200);
	    boolean first = true;
	    for (Entry<String, String> entry : data.entrySet()) {
	    	if(first){
	    		first = false;
	    	}else{
	    		sb.append('&');
	    	}
			sb.append(entry.getKey()).append('=').append(entry.getValue());
		}
	    String sign = MD5Util.md5Appkey(sb.append(safeKey).toString());
	    sb.setLength(0);
	    sb.append(QUERY_URL).append(sign)
	    .append("&out_trade_no=").append(orderID)
	    .append("&partner=").append(partner)
	    .append("&sign_type=MD5&_input_charset=utf-8");
	  //  String url = URLEncoder.encode(sb.toString(), "UTF-8");
	    HttpURLConnection connection = null;
	    InputStream inputStream = null;
	    try{
	    	logger.info(sb.toString());
	    	connection = createConnection(sb.toString());
	    	inputStream = new BufferedInputStream(connection.getInputStream());
	    	Map<String, String> xml = XMLUtil.parseXML(inputStream, false);
	    	logger.info(MapUtil.toString(xml));
	    } catch (XMLParseException e) {
			throw new IOException(e);
		}finally{
			IoUtil.close(inputStream);
	    	if(connection!=null)connection.disconnect();
	    }
	    
		return null;
	}
	

	
	private static HttpURLConnection createConnection(String url) throws IOException{
		URL postUrl = new URL(url);
		HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod("GET");
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setChunkedStreamingMode(5);
		connection.setConnectTimeout(HttpUtil.CONNECT_TIMEOUT);
		connection.setReadTimeout(HttpUtil.READ_TIMEOUT);
		connection.connect();
		return connection;
	}

	@Override
	public boolean clientVerify(String content, String deviceID) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public OrderInfo parseOrder(HttpServletRequest request) throws PayException {
		//验证有效性
		Map<String,String> params = new HashMap<String,String>();
		Map<String,String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i]
						: valueStr + values[i] + ",";
			}
			//乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}
		
		//获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		//商户订单号
		try{
			String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
			//支付宝交易号

			String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
			//交易状态
			String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
			
			if(AlipayNotify.verify(params, partner)){
				
				if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){
					OrderInfo orderInfo = new OrderInfo();
					orderInfo.setOrderId(out_trade_no);
					orderInfo.setPlatId(trade_no);
					orderInfo.setPayStatus(PayStatus.PayStatus_Success);
					return orderInfo;
				}
			}
		}catch(UnsupportedEncodingException e){
			
		}
		return null;
	}

	
	/**
	 * create the order info. 创建订单信息
	 * 
	 */
	private void getOrderInfo(StringBuilder sb,String subject, double price,String outID,String notifyUrl) {
		// 签约合作者身份ID
		sb.append("partner=").append( '\"' ).append( partner ).append( '\"')

		// 签约卖家支付宝账号
		.append( "&seller_id=" ).append( '\"' ).append( seller ).append( '\"')

		// 商户网站唯一订单号
		.append( "&out_trade_no=" ).append( '\"' ).append( outID ).append( '\"')

		// 商品名称
		.append( "&subject=" ).append( '\"' ).append( subject ).append( '\"')

		// 商品详情
		//.append( "&body=" ).append( '\"' ).append( body ).append( '\"')

		// 商品金额
		.append( "&total_fee=" ).append( '\"' ).append( price ).append( '\"')

		// 服务器异步通知页面路径
		.append( "&notify_url=" ).append( '\"' ).append( notifyUrl).append( '\"')

		// 服务接口名称， 固定值
		.append( "&service=\"mobile.securitypay.pay\"")

		// 支付类型， 固定值
		.append( "&payment_type=\"1\"")

		// 参数编码， 固定值
		.append( "&_input_charset=\"utf-8\"")

		// 设置未付款交易的超时时间
		// 默认30分钟，一旦超时，该笔交易就会自动被关闭。
		// 取值范围：1m～15d。
		// m-分钟，h-小时，d-天，1c-当天（无论交易何时创建，都在0点关闭）。
		// 该参数数值不接受小数点，如1.5h，可转换为90m。
		.append( "&it_b_pay=\"30m\"")

		// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
		// .append( "&extern_token=" ).append( '\"' ).append( extern_token ).append( '\"';

		// 支付宝处理完请求后，当前页面跳转到商户指定页面的路径，可空
		.append( "&return_url=\"m.alipay.com\"");

		// 调用银行卡支付，需配置此参数，参与签名， 固定值 （需要签约《无线银行卡快捷支付》才能使用）
		// .append( "&paymethod=\"expressGateway\"";

	}

	private static final String SIGN = "&sign=";
	@Override
	public PayStatus queryClientNotifyOrderStatus(String id,SysOrder order,  Map<String, Object> json)
			throws PayException, IOException {
		String info = (String) json.get("info");
		info = URLDecoder.decode(info, "UTF-8");
		byte[] bytes = Base64.decodeBase64(info);
		info = new String(bytes,"UTF-8");
		String target = info.substring(0,info.indexOf("&sign_type="));
		String sign = info.substring(info.indexOf(SIGN)+SIGN.length()+1);
		sign = sign.substring(0, sign.length()-1);
		//这里验证
		if(AlipayNotify.verify(target, sign)){
			String outId = getPart(info, "&out_trade_no=");
			if(id.equals(outId)){
				return PayStatus.PayStatus_Success;
			}
			throw new PayException("订单id与信息中的订单id不一致",null);
		}
		throw new PayException("签名错误",null);
	}

	private static String getPart(String info,String part){
		String sign = info.substring(info.indexOf(part)+part.length()+1);
		sign = sign.substring(0, sign.indexOf("\""));
		return sign;
	}
}
